﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using Util.Media;
using Util.Forms;
using System.IO;

namespace Test
{
    public partial class ColorQuantizationForm : Form
    {
        public ColorQuantizationForm()
        {
            InitializeComponent();
        }

        private void btnTest_Click(object sender, EventArgs e)
        {
            string filename = "../../data/" + cmbFileNames.Text;
            Bitmap bmp = new Bitmap(filename);

            int[] argbOriginal = GetBitmapData(bmp);
            int[] argbQuantized = (int[])argbOriginal.Clone();

            DateTime t1 = DateTime.Now;
            txtStatus.Text = "Quantizing...";
            OctreeColorQuantizer q = new OctreeColorQuantizer();
            q.Quantize(argbQuantized, bmp.Width, bmp.Height, Int32.Parse(cmbPaletteSize.Text));
            TimeSpan ts = DateTime.Now - t1;
            txtStatus.Text = "Done: " + ts.TotalSeconds.ToString("0.###") + " sec";

            propertyGrid1.SelectedObject = q;
            //propertyGrid1.ExpandAllGridItems();

            lblQuantized.Text = string.Format(
                "Quantized: {1} of {0} colors represented. # pixels = {2}.",
                q.DistinctColorCount,
                (from node in q.PaletteNodes select (int)node.ColorCount).Sum(),
                (from node in q.PaletteNodes select (int)node.PixelCount).Sum());

            // Render the quantized bitmap.
            Bitmap bmpNew = new Bitmap(bmp.Width, bmp.Height, PixelFormat.Format32bppArgb);
            SetBitmapData(bmpNew, argbQuantized);
            pictureBox2.Image = bmpNew;

            imageList1.Images.Clear();
            lvPalette.Items.Clear();
            foreach (OctreeNode node in q.PaletteNodes)
            {
                Color c = node.AverageColor;
                Size size = imageList1.ImageSize;
                Bitmap colorSquare = new Bitmap(size.Width, size.Height, PixelFormat.Format32bppArgb);
                using (Graphics g = Graphics.FromImage(colorSquare))
                {
                    g.FillRectangle(new SolidBrush(c), 0, 0, size.Width, size.Height);
                    g.DrawRectangle(Pens.Black, 0, 0, size.Width - 1, size.Height - 1);
                }
                imageList1.Images.Add(colorSquare);

                ListViewItem item = new ListViewItem();
                item.Text = string.Format(" {0}, {1}, {2}", c.R, c.G, c.B);
                item.SubItems.Add(node.PixelCount.ToString("#,#"));
                item.SubItems.Add(node.ColorCount.ToString("#,#"));
                item.SubItems.Add(node.Level.ToString());
                item.ImageIndex = lvPalette.Items.Count;
                item.Tag = c;
                lvPalette.Items.Add(item);
            }
        }

        private void ColorQuantizationForm_Load(object sender, EventArgs e)
        {
            lvPalette.SetWindowTheme("explorer");

            cmbFileNames.Items.AddRange(
                (from FileInfo fi in new DirectoryInfo("../../data/").GetFiles()
                 let ext = fi.Extension.ToLowerInvariant()
                 where ext == ".bmp" || ext == ".jpg"
                 select fi.Name).ToArray());
            cmbFileNames.Text = "earth.bmp";
        }

        public int[] GetBitmapData(Bitmap bmp)
        {
            int[] pixels = new int[bmp.Height * bmp.Width];
            BitmapData bmpData = bmp.LockBits(
                new Rectangle(0, 0, bmp.Width, bmp.Height),
                ImageLockMode.ReadOnly,
                PixelFormat.Format32bppArgb);
            Marshal.Copy(bmpData.Scan0, pixels, 0, pixels.Length);
            bmp.UnlockBits(bmpData);
            return pixels;
        }

        public void SetBitmapData(Bitmap bmp, int[] pixels)
        {
            BitmapData bmpData = bmp.LockBits(
                new Rectangle(0, 0, bmp.Width, bmp.Height),
                ImageLockMode.WriteOnly,
                PixelFormat.Format32bppArgb);
            Marshal.Copy(pixels, 0, bmpData.Scan0, pixels.Length);
            bmp.UnlockBits(bmpData);
        }

        public static int CountColors(int[] pixels)
        {
            var sampler = new UniformColorSampler();
            sampler.Sample(pixels, 1, pixels.Length);
            return sampler.ColorCount;
        }

        private void lvPalette_SelectedIndexChanged(object sender, EventArgs e)
        {
            Bitmap bmp = pictureBox2.Image as Bitmap;
            int[] pixels = GetBitmapData(bmp);
            if (lvPalette.SelectedItems.Count == 0)
            {
                for (int i = 0; i < pixels.Length; i++)
                    pixels[i] |= ~0x00FFFFFF;
            }
            else
            {
                int c = ((Color)lvPalette.SelectedItems[0].Tag).ToArgb() & 0x00FFFFFF;
                for (int i = 0; i < pixels.Length; i++)
                {
                    if ((pixels[i] & 0x00FFFFFF) == c)
                        pixels[i] |= ~0x00FFFFFF;
                    else
                        pixels[i] &= 0x00FFFFFF;
                }
            }
            SetBitmapData(bmp, pixels);
            pictureBox2.Image = bmp;
        }

        private void cmbFileNames_Click(object sender, EventArgs e)
        {

        }

        private void cmbFileNames_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (cmbFileNames.Text != "")
            {
                string filename = "../../data/" + cmbFileNames.Text;
                Bitmap bmp = new Bitmap(filename);
                pictureBox1.Image = bmp;
                
                int[] argbOriginal = GetBitmapData(bmp);
                lblOriginal.Text = string.Format(
                    "Original: {0} colors, {1} pixels.",
                    CountColors(argbOriginal).ToString("#,#"),
                    argbOriginal.Length.ToString("#,#"));
            }
        }

        private void btnSave_Click(object sender, EventArgs e)
        {
            Image img = pictureBox2.Image;
            if (img == null)
                return;
            if (saveFileDialog1.ShowDialog(this) != System.Windows.Forms.DialogResult.OK)
                return;
            string filename = saveFileDialog1.FileName;
            img.Save(filename, ImageFormat.Bmp);
        }

        private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
        {
            Bitmap bmp = pictureBox1.Image as Bitmap;
            if (bmp == null)
                return;

            int x = e.X * bmp.Width / pictureBox1.Width;
            int y = e.Y * bmp.Height / pictureBox1.Height;
            if (x >= bmp.Width || y >= bmp.Height)
                return;

            Color c = bmp.GetPixel(x, y);
            txtCurrentColor.Text = string.Format(
                "RGB=({0}, {1}, {2}), HSV=({3:0}, {4:0.000}, {5:0.000})",
                c.R, c.G, c.B, c.GetHue(), c.GetSaturation(), c.GetBrightness());
        }
    }
}
